package io.github.dunwu.javatech.test.junit5;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DynamicNode;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import org.junit.jupiter.api.function.ThrowingConsumer;

import java.util.*;
import java.util.function.Function;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.DynamicContainer.dynamicContainer;
import static org.junit.jupiter.api.DynamicTest.dynamicTest;

@Disabled
class DynamicTests {

    @TestFactory
    DynamicTest[] dynamicTestsFromArray() {
        return new DynamicTest[] { dynamicTest("7th dynamic test", () -> assertTrue(true)),
            dynamicTest("8th dynamic test", () -> assertEquals(4, 2 * 2)) };
    }

    @TestFactory
    Collection<DynamicTest> dynamicTestsFromCollection() {
        return Arrays.asList(dynamicTest("1st dynamic test", () -> assertTrue(true)),
                             dynamicTest("2nd dynamic test", () -> assertEquals(4, 2 * 2)));
    }

    @TestFactory
    Stream<DynamicTest> dynamicTestsFromIntStream() {
        // Generates tests for the first 10 even integers.
        return IntStream.iterate(0, n -> n + 2)
                        .limit(10)
                        .mapToObj(n -> dynamicTest("test" + n, () -> assertEquals(0, n % 2)));
    }

    @TestFactory
    Iterable<DynamicTest> dynamicTestsFromIterable() {
        return Arrays.asList(dynamicTest("3rd dynamic test", () -> assertTrue(true)),
                             dynamicTest("4th dynamic test", () -> assertEquals(4, 2 * 2)));
    }

    @TestFactory
    Iterator<DynamicTest> dynamicTestsFromIterator() {
        return Arrays.asList(dynamicTest("5th dynamic test", () -> assertTrue(true)),
                             dynamicTest("6th dynamic test", () -> assertEquals(4, 2 * 2))).iterator();
    }

    @TestFactory
    Stream<DynamicTest> dynamicTestsFromStream() {
        return Stream.of("A", "B", "C").map(str -> dynamicTest("test" + str, () -> {
            /* ... */
        }));
    }

    @TestFactory
    Stream<DynamicNode> dynamicTestsWithContainers() {
        return Stream.of("A", "B", "C")
                     .map(input -> dynamicContainer("Container " + input,
                                                    Stream.of(dynamicTest("not null", () -> assertNotNull(input)),
                                                              dynamicContainer("properties", Stream.of(
                                                                  dynamicTest("length > 0",
                                                                              () -> assertTrue(input.length() > 0)),
                                                                  dynamicTest("not empty",
                                                                              () -> assertFalse(input.isEmpty())))))));
    }

    // This will result in a JUnitException!
    @TestFactory
    List<String> dynamicTestsWithInvalidReturnType() {
        return Arrays.asList("Hello");
    }

    @TestFactory
    Stream<DynamicTest> generateRandomNumberOfTests() {

        // Generates random positive integers between 0 and 100 until
        // a number evenly divisible by 7 is encountered.
        Iterator<Integer> inputGenerator = new Iterator<Integer>() {

            Random random = new Random();

            int current;

            @Override
            public boolean hasNext() {
                current = random.nextInt(100);
                return current % 7 != 0;
            }

            @Override
            public Integer next() {
                return current;
            }
        };

        // Generates display names like: input:5, input:37, input:85, etc.
        Function<Integer, String> displayNameGenerator = (input) -> "input:" + input;

        // Executes tests based on the current input value.
        ThrowingConsumer<Integer> testExecutor = (input) -> assertTrue(input % 7 != 0);

        // Returns a stream of dynamic tests.
        return DynamicTest.stream(inputGenerator, displayNameGenerator, testExecutor);
    }

}
